package com.youlai.system.plugin.opcua;

import org.eclipse.milo.opcua.sdk.client.OpcUaClient;
import org.eclipse.milo.opcua.sdk.client.api.identity.AnonymousProvider;
import org.eclipse.milo.opcua.sdk.client.api.subscriptions.UaSubscription;
import org.eclipse.milo.opcua.sdk.client.subscriptions.ManagedDataItem;
import org.eclipse.milo.opcua.sdk.client.subscriptions.ManagedSubscription;
import org.eclipse.milo.opcua.sdk.client.subscriptions.OpcUaSubscriptionManager;
import org.eclipse.milo.opcua.stack.core.AttributeId;
import org.eclipse.milo.opcua.stack.core.UaException;
import org.eclipse.milo.opcua.stack.core.security.SecurityPolicy;
import org.eclipse.milo.opcua.stack.core.types.builtin.*;
import org.eclipse.milo.opcua.stack.core.types.builtin.unsigned.UInteger;
import org.eclipse.milo.opcua.stack.core.types.enumerated.MonitoringMode;
import org.eclipse.milo.opcua.stack.core.types.enumerated.TimestampsToReturn;
import org.eclipse.milo.opcua.stack.core.types.structured.MonitoredItemCreateRequest;
import org.eclipse.milo.opcua.stack.core.types.structured.MonitoringParameters;
import org.eclipse.milo.opcua.stack.core.types.structured.ReadValueId;

import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutionException;

public class OpcUaHelper2 {
    //opc ua服务端地址

    public OpcUaClient opcUaClient;
    public String endPointUrl;
    public boolean isConnected;

    private UaSubscription subscription;
    private OpcUaSubscriptionManager opcuaSubscriptionManager;
    private List<NodeId> subscriptionItems;
    private ManagedDataItem.DataValueListener subscriptionListener;
    private ManagedSubscription manageSubscription;

    public OpcUaHelper2(String ip, int port){
        endPointUrl = "opc.tcp://" + ip + ":" + port;
    }
    public void Connect() throws Exception {
        //创建OPC UA客户端
        opcUaClient = createClient();
        //开启连接
        opcUaClient.connect().get();
        //添加订阅管理
        manageSubscription = ManagedSubscription.create(opcUaClient);
        opcuaSubscriptionManager = opcUaClient.getSubscriptionManager();
        //添加订阅监听器，用于处理断线重连后的订阅问题
//        opcuaSubscriptionManager.addSubscriptionListener(new CustomSubscriptionListener(this));
        isConnected = true;
    }

    public void DisConnect()  {
        try {
            isConnected = false;
            SubscriptionCancel();
            opcUaClient.disconnect();
            opcuaSubscriptionManager = null;
            opcUaClient = null;
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 创建OPC UA客户端
     *
     * @return
     * @throws Exception
     */
    private OpcUaClient createClient() throws Exception {
        Path securityTempDir = Paths.get(System.getProperty("java.io.tmpdir"), "security");
        Files.createDirectories(securityTempDir);
        if (!Files.exists(securityTempDir)) {
            throw new Exception("unable to create security dir: " + securityTempDir);
        }
        return OpcUaClient.create(endPointUrl,
                endpoints ->
                        endpoints.stream()
                                .filter(e -> e.getSecurityPolicyUri().equals(SecurityPolicy.None.getUri()))
                                .findFirst(),
                configBuilder ->
                        configBuilder
                                .setApplicationName(LocalizedText.english("opc-ua")) // huazh-01
                                .setApplicationUri("") // ns=2:s=huazh-01.device1.data-huazh
                                //访问方式 new AnonymousProvider()
                                //new UsernameProvider("ua", "123456")
                                .setIdentityProvider(new AnonymousProvider())
                                .setRequestTimeout(UInteger.valueOf(5000))
                                .build()
        );
    }

//    public void Subscribe(List<String> nodeIdStrList, UaMonitoredItem.ValueConsumer callback) throws Exception {
//        //创建发布间隔100ms的订阅对象
//
//        subscription = opcuaSubscriptionManager.createSubscription(100.0).get();
//        List<MonitoredItemCreateRequest> requests = new ArrayList<>();
//        for (int i = 0; i<nodeIdStrList.size(); i++ ){
//            requests.add(createMonitoringRequest(i, nodeIdStrList.get(i)));
//        }
//        subscription.createMonitoredItems(TimestampsToReturn.Both, requests, (item, id)->{
//            item.setValueConsumer(callback);
//        });
//    }
//    public void CancelSubscribe() throws ExecutionException, InterruptedException {
//        opcuaSubscriptionManager.deleteSubscription(subscription.getSubscriptionId()).get();
//    }

    public void SetSubscriptionItems(List<String> nodeIdStrList, ManagedDataItem.DataValueListener subscriptionListener){
        subscriptionItems = new ArrayList<>();
        for(var nodeIdStr:nodeIdStrList){
            subscriptionItems.add(NodeId.parse(nodeIdStr));
        }
        this.subscriptionListener = subscriptionListener;
    }

    public void SubscriptionHandler() throws UaException {
        if(subscriptionItems != null && !subscriptionItems.isEmpty()){
            List<ManagedDataItem> dataItemList = manageSubscription.createDataItems(subscriptionItems);
            for(var item : dataItemList){
                item.addDataValueListener(subscriptionListener);
            }
        }
    }
    public void SubscriptionCancel() throws UaException {
        for(var item : manageSubscription.getDataItems()){
            item.removeDataValueListener(subscriptionListener);
        }
        manageSubscription.delete();
        subscriptionItems.clear();
        subscriptionListener = null;
    }


    public  DataValue ReadValue(String nodeIdStr) throws ExecutionException, InterruptedException {
        //"ns=3;s=\"ProductionCountData\".\"OneDayCountor\"[0]"
        NodeId nodeId = NodeId.parse(nodeIdStr);
        return opcUaClient.readValue(0.0, TimestampsToReturn.Both, nodeId).get();
    }

    public boolean WriteValue(String nodeIdStr, Object value) throws ExecutionException, InterruptedException {
        NodeId nodeId = NodeId.parse(nodeIdStr);
        //创建Variant对象和DataValue对象
        Variant v = new Variant(value);
        DataValue dataValue = new DataValue(v, null, null);
        StatusCode statusCode = opcUaClient.writeValue(nodeId, dataValue).get();
        return statusCode.isGood();
    }

    private MonitoredItemCreateRequest createMonitoringRequest(int id, String nodeIdStr){
        NodeId nodeId = NodeId.parse(nodeIdStr);
        ReadValueId readValueId = new ReadValueId(nodeId, AttributeId.Value.uid(), null, null);
        //创建监控的参数
        MonitoringParameters parameters = new MonitoringParameters(UInteger.valueOf(1 + id), 100.0, null, UInteger.valueOf(10), true);
        //创建监控项请求
        //该请求最后用于创建订阅。
        return new MonitoredItemCreateRequest(readValueId, MonitoringMode.Reporting, parameters);
    }
}
